/**
 * Copyright 2018 orivil.com. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found at https://mit-license.org.
 */

package actions

import (
	"hash"
	"github.com/google/go-querystring/query"
	"sort"
	"strings"
	"encoding/hex"
	"gitee.com/tugoer/orivil/mux"
	"gitee.com/tugoer/wechat-share/code/model"
	"time"
	"fmt"
	"gitee.com/tugoer/orivil/uuid"
	"crypto/sha1"
	"gitee.com/tugoer/orivil/bundles/admin"
	"gitee.com/tugoer/orivil/wechat/access"
)

type configSign struct {
	Noncestr    string `url:"noncestr"`
	JsapiTicket string `url:"jsapi_ticket"`
	Timestamp   int64  `url:"timestamp"`
	Url         string `url:"url"`
}

type userConfig struct {
	Debug     bool     `json:"debug"`
	AppID     string   `json:"appId"`
	Timestamp int64    `json:"timestamp"`
	NonceStr  string   `json:"nonceStr"`
	Signature string   `json:"signature"`
	JsApiList []string `json:"jsApiList"`
}

// 将结构体转换为签名，结构体字段可以使用 url 标签进行重命名
// 获得 md5 签名：getSign(schema, md5.New(), ""), 微信还规定要将结果转换为大写
// 获得 sha1 签名：getSign(schema, sha1.New(), "")
func getSign(schema interface{}, mod hash.Hash, key string) (sign string, err error) {
	vs, e := query.Values(schema)
	if e != nil {
		return "", e
	}
	//STEP 1: 对key进行升序排序，略过空值
	var keys []string
	for key, vues := range vs {
		if len(vues) > 0 && vues[0] != "" {
			keys = append(keys, key)
		}
	}
	sort.Strings(keys)

	//STEP2: 对key=value的键值对用&连接起来
	var kv string
	for _, k := range keys {
		kv += k + "=" + vs[k][0] + "&"
	}

	//STEP3, 在键值对的最后加上key=API_KEY
	if key != "" {
		kv += "key=" + key
	} else {
		kv = strings.TrimSuffix(kv, "&")
	}
	//STEP4：hash 序列化，可以为 MD5 或 SHA1，如果是 MD5 需要将结果转换为大写
	mod.Write([]byte(kv))
	cipherStr := mod.Sum(nil)
	return hex.EncodeToString(cipherStr), nil
}

var Article = []*mux.Action{
	{
		Name:   "Get Article",
		Method: "GET",
		Route:  "article-data",
		Params: mux.Parameters{
			{
				In:     mux.Query,
				Schema: &getArticle{},
			},
		},
		Handler: func(ctx *mux.Context) {

			p := &getArticle{}
			ctx.WarnParse(p)
			a := model.GetArticle(p.ID)
			if a != nil {
				w := model.GetWeChatByAppID(a.AppID)
				if w != nil {
					/**
						生成客户端配置数据
				 	*/
					now := time.Now().Unix()
					ticket := access.GetTicket(&access.Config{
						AppID: w.AppID,
						Secret: w.Secret,
					})
					if ticket.ErrCode != 0 {
						ctx.ErrEmergency(ticket.Error)
					} else {
						str := uuid.New()
						// 生成配置签名
						s := &configSign {
							Noncestr:    str,
							JsapiTicket: ticket.Value,
							Timestamp:   now,
							Url:     ctx.Request.Header.Get("Referer"),
						}
						cSign, err := getSign(s, sha1.New(), "")
						if err != nil {
							ctx.ErrEmergency(fmt.Errorf("config 签名出错：%s\n", err))
						}
						// 生成配置文件
						c := userConfig{
							Debug:     mux.Env.Server.Debug,
							AppID:     w.AppID,
							Timestamp: now,
							NonceStr:  str,
							Signature: cSign,
							JsApiList: []string{
								"checkJsApi",
								"onMenuShareTimeline",
								"onMenuShareAppMessage",
								"onMenuShareQQ",
								"onMenuShareWeibo",
							},
						}
						domain, err := model.GetDomainByHost(ctx.Query().Get("shareDomain"))
						if err != nil {
							ctx.ErrWarning(err)
						} else {
							counts := strings.Split(domain.CountScript, "|")
							for key, count := range counts {
								counts[key] = strings.TrimSpace(count)
							}
							ctx.AllowCrossSiteAccess()
							ctx.SendJson(map[string]interface{}{
								"article": a,
								"env":     c,
								"counts": counts,
							})
						}
					}
				}
			}
		},
	},
	{
		Name:   "Get Articles",
		Method: "GET",
		Route:  "admin/articles",
		Params: mux.Parameters{
			{
				In:     mux.Query,
				Schema: &getArticles{},
			},
		},
		Handler: func(ctx *mux.Context) {
			p := &getArticles{}
			ctx.WarnParse(p)
			as := model.QueryArticles(p.AppID, p.Limit, p.Offset)
			ctx.SendJson(as)

		},
	},
	{
		Name:   "Count Article",
		Method: "GET",
		Route:  "/admin/article-count",
		Params: mux.Parameters{
			{
				In:     mux.Query,
				Schema: &countArticle{},
			},
		},
		Handler: func(ctx *mux.Context) {
			p := &countArticle{}
			ctx.WarnParse(p)
			total := model.CountArticle(p.AppID)
			ctx.SendJson(map[string]int{"Total": total})
		},
	},
	{
		Name:   "Create Article",
		Method: "POST",
		Route:  "/admin/article",
		Params: mux.Parameters{
			{
				In:     mux.Form,
				Schema: &model.Article{},
			},
		},
		Handler: func(ctx *mux.Context) {
			admin := admin.GetAdminFromSession(ctx)
			if admin != nil {
				p := &model.Article{}
				ctx.WarnParse(p)
				err := p.Create(admin.ID)
				ctx.ErrWarning(err)
				ctx.SendJson(map[string]int{"ID": p.ID})
			} else {
				ctx.MsgWarning("未登陆管理员", "")
			}
		},
	},
	{
		Name:   "Update Article",
		Method: "PUT",
		Route:  "admin/article",
		Params: mux.Parameters{
			{
				In:     mux.Form,
				Schema: &model.Article{},
			},
		},
		Handler: func(ctx *mux.Context) {
			admin := admin.GetAdminFromSession(ctx)
			if admin != nil {
				p := &model.Article{}
				ctx.WarnParse(p)
				err := p.Update(admin.ID)
				ctx.ErrWarning(err)
				ctx.MsgSuccess("修改成功", "")
			} else {
				ctx.MsgWarning("未登陆管理员", "")
			}
		},
	},
	{
		Name:   "Delete Article",
		Method: "DELETE",
		Route:  "admin/article",
		Params: mux.Parameters{
			{
				In:     mux.Query,
				Schema: &delArticle{},
			},
		},
		Handler: func(ctx *mux.Context) {
			admin := admin.GetAdminFromSession(ctx)
			if admin != nil {
				p := &delArticle{}
				ctx.WarnParse(p)
				a := &model.Article{ID: p.ID}
				err := a.Delete(admin.ID)
				ctx.ErrWarning(err)
				ctx.MsgSuccess("删除成功", "")
			} else {
				ctx.MsgWarning("未登陆管理员", "")
			}
		},
	},
}

type getArticles struct {
	AppID string `required:"App ID 不能为空"`
	Limit int `required:"limit 不能为空" num:"1<=x<=50" num-msg:"1-50之间"`
	Offset int `required:"offset 不能为空"`
}

type countArticle struct {
	AppID string `required:"AppID 不能为空"`
}

type getArticle struct {
	ID int `required:"ID 不能为空"`
}

type delArticle getArticle
